﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Medora.Core;
using Medora.DataContract;
using Medora.Model;
using Medora.Model.Basic;
using Medora.Model.Institutions;
using Medora.Services.Utils;
using Translator = Medora.Services.Basic.Translator;

namespace Medora.Services.Institutions
{
    public class DepartmentsService: IDepartmentsService
    {
        private readonly IUow _uow;
        private readonly IUserService _userService;
        private readonly IFilterConstructor<DepartmentFilter, Department> _filterConstructor;

        public DepartmentsService(IUow uow, IUserService userService, IFilterConstructor<DepartmentFilter, Department> filterConstructor)
        {
            _uow = uow;
            _userService = userService;
            _filterConstructor = filterConstructor;
        }

        public Department GetById(Guid id)
        {
            return _uow.Departments.GetById(id);
        }

        public bool Save(Guid userId, Department department)
        {
            //todo add logging on save
            try
            {
                if (department.Id != Guid.Empty)
                {
                    _uow.Departments.Update(department);
                }
                else
                {
                    var user = _userService.GetById(userId);
                    if (user.InstitutionId != null)
                    {
                        department.InstitutionId = user.InstitutionId.Value;
                        department.CreatedBy = userId;
                        department.CreatedOn = DateTime.Now;
                    }
                    _uow.Departments.Add(department);
                }
                _uow.Commit();
                return true;
            }
            catch (Exception e)
            {
                Logger.WriteLog(e);
                throw  new Exception(Translator.Instance.Translate("Error during data save"));
            }
        }
        
        public IEnumerable<Department> GetAll()
        {
            return _uow.Departments.GetAll().ToList();
        }

        public IEnumerable<Department> GetByInstitution(Guid institutionId)
        {
            return _uow.Departments.Get(o =>o.Deleted!=true && o.InstitutionId == institutionId);
        }

        public IEnumerable<Department> GetByInstitutionByUserId(Guid userId)
        {
            var user = _userService.GetById(userId);
            if (user != null && user.InstitutionId.HasValue)
            {
                return GetByInstitution(user.InstitutionId.Value);
            }
            return new List<Department>();
        }

        public IPagedEntities<Department> GetPagedByInstitutionByUserId(int pageSize, int page, Guid userId, DepartmentFilter filter=null, SortInfo sortInfo=null)
        {
            var user = _userService.GetById(userId);
            if (user != null && user.InstitutionId.HasValue)
            {
                Expression<Func<Department, bool>> startE = o => o.Deleted != true && o.InstitutionId == user.InstitutionId.Value;
                var finalE = ConstructFilter(startE, filter);
                var entities = GetPagedByInstitution(pageSize, page, finalE, sortInfo);
                return new PagedEntities<Department>
                    {
                        Entities = entities,
                        Count = _uow.Departments.GetCount(finalE)
                    };
            }
            return new PagedEntities<Department> {Count = 0};
        }

        public IEnumerable<Department> GetPagedByInstitution(int pageSize, int page, Expression<Func<Department, bool>> expression, SortInfo sortInfo=null)
        {
            if (sortInfo == null || string.IsNullOrEmpty(sortInfo.Field))
            {
                sortInfo = new SortInfo { Direction = SortOrderType.Desc, Field = "CreatedOn" };
            }

            return _uow.Departments.GetTake(pageSize, page, expression, sortInfo ).ToList();
            
        }

        public int GetCount(Guid userId)
        {
            var user = _userService.GetById(userId);
            if (user != null && user.InstitutionId.HasValue)
            {
                Expression<Func<Department, bool>> startE =
                    o => o.Deleted != true && o.InstitutionId == user.InstitutionId.Value;
                return _uow.Departments.GetCount(startE);
            }
            return 0;
        }

        private Expression<Func<Department, bool>> ConstructFilter(Expression<Func<Department, bool>> startE, DepartmentFilter filter=null)
        {
            if (filter == null) return startE;
            return _filterConstructor.GetFilter(filter, startE) ?? startE;
        }

        public IEnumerable<Department> GetPaged(int pageSize, int page)
        {
            return _uow.Departments.GetTake(pageSize, page, m => m.OrderByDescending(x => x.CreatedOn), o =>o.Deleted!=true).ToList();
        } 

        public bool Delete(List<Guid> ids)
        {
            try
            {
                var departments = _uow.Departments.Get(o => ids.Contains(o.Id)).ToList();
                foreach (var department in departments)
                {
                    department.Deleted = true;
                    _uow.Departments.OneFieldUpdate(department, d => d.Deleted);    
                }
                _uow.Commit();
                return true;
            }
            catch (Exception e)
            {
                Logger.WriteLog(e);
                throw  new Exception(Translator.Instance.Translate("Error during data save"));
            }
        }
    }
}
